/*
 * SharedArray.h
 *
 *  Created on: 2012-9-27
 *      Author: yangentao@gmail.com
 */

#pragma once;
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <string>
#include <stack>
#include <vector>
#include <boost/shared_array.hpp>

#include "baseutil.h"

using namespace std;
namespace yet{
template<typename T>
class SharedArray: public WithTag {
protected:
	boost::shared_array<T> arr;
	int capcity;
	int count;
protected:
	void maybeCloneSelf(int increase = 0) {
		assert(increase >=0);
		if (!arr.unique() || (count + increase > capcity)) {
			capcity = getGrowSize(count + increase);
			T* buf = new T[capcity];
			if (count && arr.get()) {
				ArrayMove(buf, arr.get(), count); //count * 1
			}
			arr.reset(buf);
		}
	}

	virtual int getMinCapcity() {
		return 4;
	}
	virtual int getGrowSize(int need) {
		assert(need >=0);
		return Max(getMinCapcity(), need *2);
	}

public:
	SharedArray() {
		capcity = 0;
		count = 0;
	}
	virtual ~SharedArray() {

	}

	void init(int capcity_) {
		assert(capcity_ >=0);
		count = 0;
		capcity = capcity_;
		T* buf = new T[Max(capcity, getMinCapcity())];
		arr.reset(buf);
	}

	void copyFrom(const T* array, int len) {
		assert(len>=0);
		if (arr.get() && !arr.unique()) {
			arr.reset();
		}
		if (array && len) {
			if (len <= capcity) {
				ArrayMove(arr.get(), array, len); //len * 1
				count = len;
			} else {
				capcity = len;
				T* buf = new T[capcity];
				ArrayMove(buf, array, len); //len * 1
				arr.reset(buf);
				count = len;
			}
		} else {
			count = 0;
		}
	}
	//will delete[] s, when ShareVector Objects destructor
	void manage(T* array, int len) {
		assert(len>=0);
		assert( array);
		arr.reset(array);
		count = len;
		capcity = len;
	}

	inline const T* ptr()const{
		return arr.get();
	}
	inline const T* getBufferRead() const {
		return arr.get();
	}
	inline T* getBufferWrite() {
		maybeCloneSelf();
		return arr.get();
	}

	void ensureCapcity(int cap) {
		if (cap > capcity) {
			maybeCloneSelf(Max(0, cap - count));
		}
	}

	inline void clear() {
		capcity = count = 0;
		arr.reset();
	}

	inline T first() const {
		assert(!empty());
		return get(0);
	}
	inline T& first() {
		assert(!empty());
		return getRef(0);
	}
	inline T last() const {
		assert(!empty());
		return get(count - 1);
	}
	inline T& last() {
		assert(!empty());
		return getRef(count - 1);
	}
	inline void setLength(int len) {
		assert(len>=0);
		assert( len <= capcity);
		count = len;
	}
	inline int length() const {
		return count;
	}
	inline int size() const {
		return count;
	}
	inline int getCapcity() const {
		return capcity;
	}
	inline bool empty() const {
		return count == 0;
	}
	inline T get(int index) const {
		assert(index>=0);
		assert( index < count);
		return arr[index];
	}
	inline T operator[](int index) const {
		assert(index >=0);
		return get(index);
	}

	inline T& operator[](int index) {
		assert( index >= 0);
		return getRef(index);
	}

	T& getRef(int index) {
		assert(index>=0);
		assert( index <= count);
		if (index == count) {
			appendRef(T());
		} else {
			maybeCloneSelf();
		}
		return arr[index];
	}
	void set(int index, const T& val) {
		assert(index>=0);
		assert( index <= count);
		if (index == count) {
			appendRef(val);
		} else {
			maybeCloneSelf();
			arr[index] = val;
		}
	}

	inline void appendRef(const T& val) {
		replace(count, 0, &val, 1);
	}

	inline void insertRef(int index, const T& val) {
		assert(index>=0);
		replace(index, 0, &val, 1);
	}

	void remove(int index) {
		assert(index>=0);
		replace(index, 1, 0, 0);
	}
	void remove(int index, int size) {
		assert(index>=0);
		assert(size>=0);
		replace(index, size, 0, 0);
	}

	void replace(int from, int length, const T* values, int valuesCount) {
		assert(from>=0);
		assert(length>=0);
		assert(valuesCount>=0);
		if (!length && !valuesCount) { //modify nothing
			return;
		}
		assert(from + length <= count);

		int newCount = count - length + valuesCount;
		if (arr.get() && arr.unique() && newCount <= capcity) {
			ArrayMove(arr.get() + from + valuesCount, arr.get() + from + length, count - from - length);
			ArrayMove(arr.get() + from, values, valuesCount);
		} else {
			int newCap = getGrowSize(newCount); // Max(newCount * 2, Max(capcity, 8));
			T* array = new T[newCap];
			if (arr.get()) {
				ArrayMove(array, arr.get(), from);
			}
			ArrayMove(array + from, values, valuesCount);
			if (arr.get()) {
				ArrayMove(array + from + valuesCount, arr.get() + from + length, count - from - length);
			}
			arr.reset(array);
			capcity = newCap;
		}
		count = newCount;

	}

	inline bool equals(const T* other, int len) const {
		assert(len>=0);
		if (length() == len) {
			return rangeEquals(0, other, len);
		}
		return false;
	}
	//[from, from+len) eq [other, other+len);
	bool rangeEquals(int from, const T* other, int len) const {
		assert(len>=0);
		assert(from>=0);
		if (from + len <= length()) {
			if (len) {
				if (other) {
					for (int i = 0; i < len; ++i) {
						if (!(arr.get()[i + from] == other[i])) {
							return false;
						}
					}
					return true;
				} else {
					if (from == length()) {
						return true;
					}
				}
			} else {
				return true;
			}
		}
		return false;
	}

	void fill(int from, const T& val) {
		assert(from>=0);
		assert(from < count);
		maybeCloneSelf();
		RangeSet(arr.get(), count - from, val);
	}
	void fill(int from, int len, const T& val) {
		assert(from>=0);
		assert(len>=0);
		assert(from + len <= count);
		if (len) {
			maybeCloneSelf();
			RangeSet(arr.get(), from, len, val);
		}
	}
};

}
